﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace UnityModule
{
    /// <summary>
    /// 大池
    /// </summary>
    public class SpawnPool : MonoBehaviour
    {
        /// <summary>
        /// 当开启平滑销毁时,每帧最多销毁多少个
        /// </summary>
        [Range(1, 20)]
        public int m_SmoothDestroyCountPerFrame = 10;

        /// <summary>
        /// 当开启平滑删除时,这里保存待删除表
        /// </summary>
        private LinkedList<GameObject> m_NeedDestroys = new LinkedList<GameObject>();

        /// <summary>
        /// ResID唯一id
        /// </summary>
        private Dictionary<uint, PrefabPool> m_PrefabPools = new Dictionary<uint, PrefabPool>();

        /// <summary>
        /// 当回池马上又spawn setactive fase ture 会失败,这里过度一下
        /// </summary>
        private LinkedList<WaitDespawn> m_WaitDespawnList = new LinkedList<WaitDespawn>();
                
        public void Update()
        {
            if (m_NeedDestroys.Count > 0)
            {
                for (int i = 0; i < m_SmoothDestroyCountPerFrame; ++i){
                    if (m_NeedDestroys.Count > 0){
                        GameObject obj = m_NeedDestroys.First.Value;
                        m_NeedDestroys.RemoveFirst();
                        if (null != obj){
                            GameObject.Destroy(obj);
                        }
                    }
                    else{
                        break;
                    }
                }
            }

            if(m_WaitDespawnList.Count > 0)
            {
                LinkedListNode<WaitDespawn> curNode = m_WaitDespawnList.First;
                while (null != curNode){
                    WaitDespawn item = curNode.Value;
                    if (item.CanDispose){
                        m_WaitDespawnList.RemoveFirst();
                        ToDespawn(item.ResId, item.Obj);
                        ReferencePoolManager.Release(item);
                        curNode = m_WaitDespawnList.First;
                    }
                    else{
                        while (null != curNode){
                            curNode.Value.CanDispose = true;
                            curNode = curNode.Next;
                        }
                    }
                }
            }
        }

        private int m_capacity;
        private Transform m_defaultParent;
        private PrefabReleaseDisappearType m_dType;
        private SpawnItemScheduler.RequestPriority m_priority;
        //初始化设置池参数
        public void Init(int capacity, Transform defaultParent, PrefabReleaseDisappearType dType, SpawnItemScheduler.RequestPriority priority)
        {
            m_capacity = capacity;
            m_defaultParent = defaultParent;
            m_dType = dType;
            m_priority = priority;
        }

        //使用固定参数加载对象
        public LoadTask Spawn(uint resId, string pathName, string prefabName, Action<GameObject> callBack)
        {
            return Spawn(resId, pathName, prefabName, m_capacity, m_defaultParent, m_dType, m_priority, callBack);
        }

        //使用不同参数加载对象
        public LoadTask Spawn(uint resId, string pathName, string prefabName, int capacity, Transform defaultParent, PrefabReleaseDisappearType dType, SpawnItemScheduler.RequestPriority priority, Action<GameObject> callBack)
        {
            if (SpawnSync(resId, callBack)){
                return null;
            }
            else {
                return SpawnAsync(resId, pathName, prefabName, capacity, defaultParent, dType, priority, callBack);
            }
        }

        //同步获取池中对象
        private bool SpawnSync(uint resId, Action<GameObject> callBack)
        {
            //只获取当前存在的空闲对象, 如果需要克隆, 则放到队列中
            PrefabPool pool = GetPool(resId);
            if (pool != null && pool.ObjCount > 0){
                callBack?.Invoke(pool.Spawn());
                return true;
            }
            return false;
        }

        //异步步获取池中对象
        private LoadTask SpawnAsync(uint resId, string pathName, string prefabName, int capacity, Transform defaultParent, PrefabReleaseDisappearType dType, SpawnItemScheduler.RequestPriority priority, Action<GameObject> callBack)
        {
            LoadTask prefabTask = new LoadTask();
            SpawnPrefabInfo info = ReferencePoolManager.Get<SpawnPrefabInfo>();
            info.Priority = priority;
            info.SetLoadTask(prefabTask);
            info.SetCallBack(callBack);
            
            CreatePoolAsync(resId, pathName, prefabName, capacity, defaultParent, dType, priority,
            (pool) =>{
                if (!prefabTask.IsDispose){
                    if (pool != null)
                    {
                        info.SetPoolWatcher(pool.PoolWatcher);
                    }
                    else
                    {
                        P.E($"SpawnPool 's pool is null , resId:{resId},pathName:{pathName},prefabName:{prefabName},defaultParent:{defaultParent},dType:{dType},priority:{priority}");
                    }
                    SpawnItemScheduler.Instance.Spawn(info);
                }
                else {
                    ReferencePoolManager.Release(info);
                }
            });
            return prefabTask;
        }

        //获取指定池, 没有则创建
        private LoadTask CreatePoolAsync(uint resId, string pathName, string prefabName, int capacity, Transform defaultParent, PrefabReleaseDisappearType dType, SpawnItemScheduler.RequestPriority priority, Action<PrefabPool> callBack)
        {
            //尝试获取池
            PrefabPool pool = GetPool(resId);
            if (pool != null){
                callBack?.Invoke(pool);
                return null;
            }
            else{
                //Debug.LogError("CreatePoolAsync" + resId + pathName + prefabName);
                LoadOptions loadOption = (priority == SpawnItemScheduler.RequestPriority.High) ? LoadOptions.HightPriority : LoadOptions.Auto;

                //池不存在, 需要创建
                //备忘 当加载资源是上个场景加的,此时场景动态场池会多一个prefab在这里,可以先忽略
                return ResourceComponent.Instance.LoadResource(pathName, prefabName, (obj) =>{
                    //加载成功创建池
                    if (obj != null){
                        CreatePrefabPool(resId, obj.obj, capacity, true, () =>{
                            pool = GetPool(resId);
                            //Debug.LogError("UnloadPool " + resId + " " + pool.m_AsynCount + pathName +" "+prefabName);
                            for(int i = 0;i<pool.m_AsynCount;i++)
                            {
                                ResourceComponent.Instance.UnloadRes(pathName, prefabName, true);
                            }
                        },
                        defaultParent, dType);
                    }

                    //再次尝试获取池
                    //返回结果
                    callBack?.Invoke(GetPool(resId));
                },
                loadOption);
            }
        }
        
        private PrefabPool GetPool(uint resId)
        {
            PrefabPool pool;
            m_PrefabPools.TryGetValue(resId, out pool);
            return pool;
        }

        /// <summary>
        /// despawn不能立马进池,否则有active false true 问题
        /// </summary>
        /// <param name="resId"></param>
        /// <param name="trm"></param>
        public void Despawn(uint resId, GameObject obj)
        {
            if (resId == 0 || null == obj){
                Debug.LogError("despawn 错误 resid=" + resId);
                if(null != obj){
                    GameObject.Destroy(obj);
                }
                return;
            }
            PrefabPool pool;
            if (m_PrefabPools.TryGetValue(resId, out pool)){
                pool.PreDespawn(obj);
                if(pool.DisappearType == PrefabReleaseDisappearType.ActiveFalse){
                    WaitDespawn item = ReferencePoolManager.Get<WaitDespawn>();
                    item.Init(resId, obj);
                    m_WaitDespawnList.AddLast(item);
                }
                else{
                    pool.Despawn(obj);
                }
            }
            else{
                GameObject.Destroy(obj);
            } 
        }

        private void ToDespawn(uint resId, GameObject obj)
        {
            PrefabPool pool;
            if (m_PrefabPools.TryGetValue(resId, out pool)){
                pool.Despawn(obj);
            }
            else{
                GameObject.Destroy(obj);
            }
        }


        /// <summary>
        /// 当内存吃紧时清除克隆好的没有用到的
        /// 是同步删除的
        /// </summary>
        public void ClearUnused()
        {
            foreach(var item in m_PrefabPools){
                item.Value.ClearUnused();
            }
            ClearWaitDespawnList();
            ClearNeddDestroyList();
        }

        public void DestroyAllPrefabPool()
        {
            foreach (var item in m_PrefabPools){
                item.Value.Destroy();
            }
            m_PrefabPools.Clear();

            ClearNeddDestroyList();
            ClearWaitDespawnList();
        }

        private void OnDestroy()
        {
            DestroyAllPrefabPool();
        }

        private void ClearNeddDestroyList()
        {
            while (m_NeedDestroys.Count > 0){
                GameObject obj = m_NeedDestroys.First.Value;
                m_NeedDestroys.RemoveFirst();
                if (null != obj){
                    GameObject.Destroy(obj);
                }
            }
        }

        private void ClearWaitDespawnList()
        {
            while (m_WaitDespawnList.Count > 0){
                WaitDespawn item = m_WaitDespawnList.First.Value;
                m_WaitDespawnList.RemoveFirst();
                if (null != item.Obj){
                    GameObject.Destroy(item.Obj);
                }
                ReferencePoolManager.Release(item);
            }
        }


        public void DestroyPrefabPool(uint resId)
        {
            PrefabPool pool;
            if (m_PrefabPools.TryGetValue(resId, out pool)){
                pool.Destroy();
                m_PrefabPools.Remove(resId);
            }
        }

        public bool ContainPrefabPool(uint resId)
        {
            return m_PrefabPools.ContainsKey(resId);
        }

        /// <summary>
        /// 不要返回PrefabPool
        /// </summary>
        /// <param name="resId"></param>
        /// <param name="prefab"></param>
        /// <param name="cullAbove"></param>
        /// <param name="smoothDestroy"></param>
        /// <param name="unloadHandler"></param>
        private void CreatePrefabPool(uint resId, UnityEngine.Object prefab, int cullAbove, bool smoothDestroy, Action unloadHandler, Transform defaultParent = null, PrefabReleaseDisappearType dType = PrefabReleaseDisappearType.ActiveFalse)
        {
            if (!ContainPrefabPool(resId)){
                Transform parentTrm = (defaultParent != null) ? defaultParent : transform;
                PrefabPool prefabPool = new PrefabPool(prefab, parentTrm, cullAbove, smoothDestroy, OnNeedDestroy, unloadHandler, dType);
                m_PrefabPools[resId] = prefabPool;
            }
            else
            {
                m_PrefabPools[resId].m_AsynCount++;
            }
        }

        private void OnNeedDestroy(GameObject obj)
        {
            if (null != obj){
                m_NeedDestroys.AddLast(obj);
            }
        }

        private class WaitDespawn : IReference
        {
            public GameObject Obj;
            public bool CanDispose;
            public uint ResId;

            public void Init(uint resId, GameObject obj)
            {
                Obj = obj;
                CanDispose = false;
                ResId = resId;
            }

            public void Clear()
            {
                CanDispose = false;
                Obj = null;
                ResId = 0;
            }
        }
    }
}

